package com.example.demo.service.impl;

import com.example.demo.dao.RoutingMapper;
import com.example.demo.model.Node;
import com.example.demo.model.Router;
import com.example.demo.model.LinkNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.example.demo.service.RoutingService;

import java.util.*;

/**
 * @author ：Docin
 * @date ：Created in 2020/9/11 10:57
 * @description：
 * @modified By：
 */

@Component
public class RoutingServiceImpl implements RoutingService{

    //常量
    final static double K1 = 0.4;
    final static double K2 = 0.3;
    final static double K3 = 0.3;
    final static double LS = 6;

    final static double A1 = 0.6;
    final static double A2 = 0.2;
    final static double A3 = 0.1;
    final static double A4 = 0.1;
    final static double IDEAL_D = 8;

    final static int NO_CONNECTION = -1;
    final static int F_ASYM = 0;
    final static int B_ASYM = 1;
    final static int SYM = 2;

    @Autowired
    RoutingMapper routingMapper;

    @Override
    public List<Node> queryterminal(int page, int limit){
        page=(page-1)*limit;
        return routingMapper.queryterminal(page, limit);
    }

    @Override
    public int queryterminalCount(){
        return routingMapper.queryterminalCount();
    }

    @Override
    public List<Node> allNodes(){
        return routingMapper.allNodes();
    }

    @Override
    public Node getNode(Integer id){
        return routingMapper.getNode(id);
    }

    @Override
    //获取指定ID的节点组
    public List<Node> getsomeNodes (Integer[] ids){
        List<Node> reqNodes = new ArrayList<Node>();
        for (int i = 0; i < ids.length; i++)
            reqNodes.add(routingMapper.getNode(ids[i]));
        return reqNodes;
    }

    @Override
    //前两位分别是班长节点和备用节点
    public Node[] election(Node[] allnodes){
        Node leader[] = new Node[2];
        double record[] = {0,0};
        for (int i = 0; i < allnodes.length; i++) {
            double votepoint = K1*allnodes[i].getRank() + K2*allnodes[i].getRange() + K3*allnodes[i].getPower();
            if (votepoint > record[0]){
                record[1] = record[0];
                record[0] = votepoint;
                leader[1] = leader[0];
                leader[0] = allnodes[i];
            }
            else if (votepoint > record[1]){
                record[1] = votepoint;
                leader[1] = allnodes[i];
            }
        }
        return leader;
    }

    @Override
    //路由表初始化
    public void initRout (Node[] nd){
        for (int j = 0; j < nd.length; j++) {
            nd[j].routers = new Router[nd.length];
            for (int i = 0; i < nd.length; i++) {
                nd[j].routers[i] = new Router();
                nd[j].routers[i].setDst(nd[i]);
                nd[j].routers[i].setDstId(nd[i].getId());
                nd[j].routers[i].setNext(null);
                nd[j].routers[i].setNextId(-1);
                nd[j].routers[i].setMetric(999);

                nd[j].routers[i].setNext2(null);
                nd[j].routers[i].setNextId2(-1);
                nd[j].routers[i].setMetric2(999);
            }
            //设置到自己的路由
            nd[j].routers[j].setNext(nd[j]);
            nd[j].routers[j].setNextId(nd[j].getId());
            nd[j].routers[j].setMetric(0);
            nd[j].routers[j].setState1(SYM);
        }
    }

    @Override
    //获取邻居节点
    public void getNeibNodes(Node[] allnodes){
        for (int i = 0; i < allnodes.length; i++) {
            Node nd = allnodes[i];
            for (int j = 0; j < allnodes.length; j++) {
                if (allnodes[j].getId() == nd.getId())  continue;
                else {
                    double dist = distanceCal(nd, allnodes[j]);
                    /*double rs = Math.sqrt(Math.pow((nd.getVelocity_x()-nd.neibNodes.get(i).getNode().getVelocity_x()),2)
                            + Math.pow((nd.getVelocity_y()-nd.neibNodes.get(i).getNode().getVelocity_y()),2));*/
                    if (dist < nd.getRange()){
                        LinkNode ln = new LinkNode(allnodes[j], dist, -1);
                        nd.neibNodes.add(ln);
                    }
                }
            }
        }
    }

    @Override
    //广播获取与邻节点的链路状态
    public void broadcast(Node nd){
        Random random = new Random();
        for (int i = 0; i < nd.neibNodes.size(); i++) {
            LinkNode dst = nd.neibNodes.get(i);
            double intensity = this.powerCal(nd, dst.getDist());
            double interference = random.nextGaussian()/3+2;        //调整干扰值
            if (intensity > interference){
                if (dst.getLinkstate() == B_ASYM)   dst.setLinkstate(SYM);
                else dst.setLinkstate(F_ASYM);
                //把对应的对面节点的state设为B_ASYM    要先遍历获得邻居再广播     有点绕。。。
                for (int j = 0; j < dst.getNode().neibNodes.size(); j++) {
                    if (dst.getNode().neibNodes.get(j).getNode().getId() == nd.getId()){
                        if (dst.getNode().neibNodes.get(j).getLinkstate() == NO_CONNECTION)
                            dst.getNode().neibNodes.get(j).setLinkstate(B_ASYM);
                        else if (dst.getNode().neibNodes.get(j).getLinkstate() == F_ASYM)
                            dst.getNode().neibNodes.get(j).setLinkstate(SYM);
                    }
                }
            }
        }
    }

    @Override
    public void routing(Node[] allnodes){
        //已有的邻居存入路由表
        for (int i = 0; i < allnodes.length; i++) {
            LinkNode tmp[] = new LinkNode[allnodes[i].neibNodes.size()];
            allnodes[i].neibNodes.toArray(tmp);
            for (int j = 0; j < tmp.length; j++) {
                if (tmp[j].getLinkstate() == F_ASYM || tmp[j].getLinkstate() == SYM){
                    Node nd = tmp[j].getNode();
                    allnodes[i].setRouter(nd.getId(), nd, 1, tmp[j].getLinkstate());
                }
            }
        }
        //邻节点广播进行二跳范围内的路由表计算
        int num = allnodes.length;

        for (int i = 0; i < num; i++) {
            int linkedNodes[][] = allnodes[i].getLinkedbNodes(i);
            for (int j = 0; j < allnodes[i].neibNodes.size(); j++) {
                LinkNode tmp = allnodes[i].neibNodes.get(j);
                if (tmp.getLinkstate() == SYM)
                    tmp.getNode().receiveRoutInfo(linkedNodes, allnodes);
            }
        }
    }

    @Override
    //设置节点位置
    public void setPosAndSpeed(Map<Integer, Object> cluster, int clusNum){
        Random random = new Random();
        double dx = 170 / clusNum;
        double dy = 100 / clusNum;
        int[] xi = new int[clusNum];
        int[] yi = new int[clusNum];
        for (int i = 0; i < clusNum; i++) {
            xi[i] = random.nextInt(clusNum);
            yi[i] = random.nextInt(clusNum);
        }
        //查看位置是否重合
        for (int i = 0; i < clusNum; i++) {
            for (int j = i+1; j < clusNum; j++) {
                //如果重合
                if (xi[i] == xi[j] && yi[i] == yi[j]){
                    xi[i] = random.nextInt(clusNum);
                    boolean flag = true;
                    while (flag){
                        flag = false;
                        for (int k = 0; k < clusNum; k++) {
                            if (i == k) continue;
                            if (xi[i] == xi[k] && yi[i] == yi[k]){
                                flag = true;
                                xi[i] = random.nextInt(clusNum);
                                break;
                            }
                        }
                    }
                }
            }
        }
        for (int i = 0; i < clusNum; i++) {
            List<Node> tmp = (List<Node>) cluster.get(i);
            double headx = 17 + xi[i]*dx;
            double heady = 10 + yi[i]*dy;
            tmp.get(0).setX(headx);
            tmp.get(0).setY(heady);
            for (int j = 1; j < tmp.size(); j++) {
                //设置X坐标
                double temp = random.nextDouble()-0.5;
                double tmpx;
                if (temp < 0.1 && temp > 0)
                    tmpx = headx + (random.nextDouble()-0.4)*dx;
                else if (temp < 0 && temp > -0.1)
                    tmpx = headx + (random.nextDouble()-0.6)*dx;
                else
                    tmpx = headx + (random.nextDouble()-0.5)*dx;

                tmp.get(j).setX(tmpx);
                //设置Y坐标
                temp = random.nextDouble()-0.5;
                double tmpy;
                if (temp < 0.1 && temp > 0)
                    tmpy = heady + (random.nextDouble()-0.4)*dy;
                else if (temp < 0&& temp > -0.1)
                    tmpy = headx + (random.nextDouble()-0.6)*dy;
                else
                    tmpy = headx + (random.nextDouble()-0.5)*dy;

                tmp.get(j).setY(tmpy);

            }
        }

        //设置节点速度
    }

    @Override
    //连编制分簇算法
    public Map<Integer, Object> wca(Node[] nodes){
        int clusterNum = nodes.length / 4;
        if (nodes.length%4 > 2)
            clusterNum = clusterNum + 1;

        double value[] = new double[nodes.length];
        //计算各节点数值
        for (int i = 0; i < nodes.length; i++)
            value[i] = wca_Cal(nodes[i]);
        //将得分排序
        int[] index = valSort(value);
        Node[] head = new Node[clusterNum];
        Node[] norNode = new Node[nodes.length - clusterNum];
        //找出簇头节点
        for (int i = 0; i < clusterNum; i++)
            head[i] = nodes[index[i]];
        //非簇头节点集合
        for (int i = clusterNum; i < nodes.length; i++)
            norNode[i-clusterNum] = nodes[index[i]];
        //分簇数组
        int[] clasf = new int[norNode.length];
        for (int i = 0; i < norNode.length; i++) {
            double disRef = 9999;
            for (int j = 0; j < clusterNum; j++) {
                double dist = distanceCal(norNode[i], head[j]);
                if (dist < disRef){
                    disRef = dist;
                    clasf[i] = j;
                }
            }
        }
        Map<Integer, Object> clusters = new HashMap<Integer, Object>();
        for (int i = 0; i < head.length; i++) {
            List<Node> clus = new ArrayList<Node>();
            clus.add(head[i]);
            for (int j = 0; j < norNode.length; j++) {
                if (clasf[j] == i)
                    clus.add(norNode[j]);
            }
            clusters.put(i,clus);
        }
        setPosAndSpeed(clusters,clusterNum);
        return clusters;
    }

    @Override
    public String itos(Integer[] s){
        String[]ss = new String[s.length];
        for (int i=0;i<ss.length;i++){
            ss[i] = Integer.toString(s[i]);
        }
        return String.join(",",ss);
    }

    @Override
    public Integer[] stoi(String ss){
        String []s =ss.split(",");
        Integer[] ints = new Integer[s.length];
        for(int i=0;i<s.length;i++){
            ints[i] = Integer.parseInt(s[i]);
        }
        return ints;

    }

    //距离计算

    public double distanceCal(Node n1, Node n2){
        double tmp = Math.pow((n1.getX()-n2.getX()),2) + Math.pow((n1.getY()-n2.getY()),2);
        double distance = Math.sqrt(tmp);
        return distance;
    }

    //接收功率计算

    public double powerCal(Node src, double d){
        double intensity = src.getPower()/(d*d* LS);
        return intensity;
    }

    //分簇簇头选举算法
    public double wca_Cal(Node nd){
        //度参数(划掉)，改为功率参数
        double pdc = Math.abs(nd.getPower()/5000 - IDEAL_D);
        //距离之和
        double dissum = 0;
        //移动性
        double relative_speed = 0;
        //指挥等级差值
        int lv= 4 - nd.getRank();
        for (int i = 0; i < nd.neibNodes.size(); i++) {
            if (nd.neibNodes.get(i).getLinkstate() == SYM){
                dissum = dissum + nd.neibNodes.get(i).getDist();
                relative_speed = relative_speed + nd.neibNodes.get(i).getReSpeed();
            }
        }
        double wca = A1*pdc + A2*lv + A3*dissum + A4*relative_speed;
        return wca;
    }

    //排序
    public int[] valSort(double[] val){
        int[] sort = new int[val.length];
        HashMap map = new HashMap();
        for (int i = 0; i < val.length; i++) {
            map.put(val[i], i); // 将值和下标存入Map
        }
        ArrayList list = new ArrayList();
        Arrays.sort(val); // 升序排列
        for (int i = val.length-1; i >= 0; i--) {
            sort[i] = (Integer) map.get(val[i]);
        }
        return sort;
    }
}
